package com.ghostflying.groundstation;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.content.Context;
import android.os.Handler;
import android.util.Log;

public class NetworkService {
	// private final Handler mHandler;
	private int mState;
	private final Handler mHandler;

	public static final int STATE_NONE = 0; // we're doing nothing
	public static final int STATE_LISTEN = 1; // now listening for incoming
												// connections
	public static final int STATE_CONNECTING = 2; // now initiating an outgoing
													// connection
	public static final int STATE_CONNECTED = 3; // now connected to a remote
													// device
	public boolean is_frame_done = true;
	private static final String TAG = "WLanService";
	private ServerThread mServerThread = null;
	private ConnectThread mConnectThread = null;
	private ConnectedThread mConnectedThread = null;
	private reConnectThread mreConnectThread = null;
	private static final boolean D = true;

	public NetworkService(Context context, Handler handler) {
		mState = STATE_NONE;
		mHandler = handler;
	}

	private synchronized void setState(int state) {
		if (D)
			Log.d(TAG, "setState() " + mState + " -> " + state);
		mState = state;

		// Give the new state to the Handler so the UI Activity can update
	}

	public synchronized int getState() {
		return mState;
	}

	public void StartListen(int port) {
		mServerThread = null;
		mServerThread = new ServerThread(port);
		setState(STATE_LISTEN);
		mServerThread.start();

	}

	public synchronized void Connect(String ipadd, int port) {
		if (D)
			Log.d(TAG, "connect to: " + ipadd + port);
		if (mState == STATE_CONNECTING) {
			if (mConnectThread != null) {
				mConnectThread.cancel();
				mConnectThread = null;
			}
		}

		mConnectThread = new ConnectThread(ipadd, port);
		mConnectThread.start();
		setState(STATE_CONNECTING);

	}
	
    private synchronized void reConnect(int port){
    	if (D) Log.d(TAG, "reconnect to: " +port);
    	setState(STATE_NONE);    	
    	mHandler.obtainMessage(MainActivity.VIDEO_DISCONNECTED,-1, -1, null).sendToTarget();
    	if (mConnectedThread != null){
    		mConnectedThread.cancel();
    		mConnectedThread = null;
    	}
    	if (mreConnectThread != null){
    	}
    	mreConnectThread = new reConnectThread (port);	    	
    	mreConnectThread.start();
    }
    
    private class reConnectThread extends Thread{
    	int mport;
    	
    	public reConnectThread (int port){
    		mport = port;
    	}
    	public void run(){
    		Log.i(TAG, "BEGIN mreConncetThread");
    		try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    		synchronized (this){
				mConnectThread = null;
			}
    		StartListen(mport);
    	}
    }

	public void write(byte[] out) {
		// Create temporary object
		ConnectedThread r;
		// Synchronize a copy of the ConnectedThread
		synchronized (this) {
			if (mState != STATE_CONNECTED)
				return;
			r = mConnectedThread;
		}
		// Perform the write unsynchronized
		r.write(out);
	}

	private class ServerThread extends Thread { // 锟教筹拷锟竭筹拷
		int mport;
		ServerSocket mserverSocket = null;
		private Socket msocket = null;

		public ServerThread(int port) {
			mport = port;
		}

		public void run() {
			Log.i(TAG, "BEGIN mServertThread");

			try {
				mserverSocket = new ServerSocket(mport);

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			while (mState != STATE_CONNECTED) {
				try {
					msocket = mserverSocket.accept();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (msocket != null) {
					if (D)
						Log.d(TAG, "accepted");
					synchronized (this) {
						switch (mState) {
						case STATE_LISTEN:
						case STATE_CONNECTING:
							// Situation normal. Start the connected thread.
							connected(msocket);
							break;
						case STATE_NONE:
						case STATE_CONNECTED:
							// Either not ready or already connected. Terminate
							// new socket.
							try {
								msocket.close();
							} catch (IOException e) {

								Log.e(TAG, "Could not close unwanted socket", e);
							}
							break;
						}
					}
				}
			}
		}

		public void cancel() {
			if (D)
				Log.d(TAG, "cancel " + this);
			try {
				if (mserverSocket != null){
					mserverSocket.close();
				}				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	public synchronized void connected(Socket socket) {
		// TODO Auto-generated method stub
		if (D)
			Log.e(TAG, "start connected");
		// Cancel the thread that completed the connection
		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread != null) {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		if (mServerThread != null) {
			mServerThread.cancel();
			mServerThread = null;
		}
		mConnectedThread = new ConnectedThread(socket);
		mConnectedThread.start();
		// mHandler.obtainMessage(InstrumentControl.MESSAGE_TOAST, 1, -1,
		// R.string.WLAN_connected).sendToTarget();
		setState(STATE_CONNECTED);
	}

	private class ConnectThread extends Thread { // 锟斤拷锟斤拷锟竭筹拷
		private String mipadd;
		private int mport;
		private Socket mSocket;

		public ConnectThread(String ipadd, int port) {
			mipadd = ipadd;
			mport = port;
		}

		public void run() {
			try {
				mSocket = new Socket(mipadd, mport);

			} catch (IOException e) {
				try {
					mSocket.close();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
			synchronized (this) {
				mConnectThread = null;
			}
			connected(mSocket);

		}

		public void cancel() {
			// TODO Auto-generated method stub
			try {
				mSocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private class ConnectedThread extends Thread { // 锟斤拷锟斤拷锟斤拷锟斤拷锟竭筹拷
		private Socket mSocket;
		private InputStream mmInStream;
		private OutputStream mmOutStream;

		public ConnectedThread(Socket socket) {
			// TODO Auto-generated constructor stub
			mSocket = socket;
			InputStream tmpIn = null;
			OutputStream tmpOut = null;

			if (mSocket == null) {
				// mHandler.obtainMessage(InstrumentControl.MESSAGE_TOAST, 1,
				// -1, R.string.WLAN_add_error).sendToTarget();
			} else {
				try {
					tmpIn = mSocket.getInputStream(); // 锟斤拷锟斤拷锟斤拷锟斤拷锟�
					tmpOut = mSocket.getOutputStream();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			mmInStream = tmpIn;
			mmOutStream = tmpOut;

		}

		public void run() {
			Log.i(TAG, "BEGIN mConnectedThread");
			mHandler.obtainMessage(MainActivity.VIDEO_CONNECTED,-1, -1, null).sendToTarget();
			byte[] buffer = new byte[1000000];
			byte[] message_buffer = new byte[1000000];

			int length = 0;


			while (true) {
				if (!readByteLength(mSocket,mmInStream, 4, buffer)){
					break;
				}
				length = b2i(buffer);
				if (!readByteLength(mSocket,mmInStream, length, buffer)){
					break;
				}
				Log.d(TAG, "read one frame end." + Integer.toString(length));
				if (is_frame_done) {
					is_frame_done = false;
					System.arraycopy(buffer, 0, message_buffer, 0, length);
					mHandler.obtainMessage(MainActivity.VIDEO_BYTE_REC, length,
							-1, message_buffer).sendToTarget();
					Log.d(TAG, "send one message." + Integer.toString(length));
				}

			}
		}

		public void write(byte[] buffer) { // write锟斤拷锟斤拷byte
			try {
				Log.e(TAG, "start write ");
				mmOutStream.write(buffer);
			} catch (Exception e) {
				Log.e(TAG, "Exception during write", e);
				e.printStackTrace();				
			}
		}

		public void cancel() { // 锟截憋拷socket
			// TODO Auto-generated method stub
			try {
				mSocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private boolean readByteLength(Socket mSocket,InputStream in, int length, byte[] b) {
		int readcount = 0;
		Log.e(TAG, "length = " + Integer.toString(length));
		while (readcount < length) {
			try {
				mSocket.setSoTimeout(2000);
				readcount += in.read(b, readcount, length - readcount);
				Log.e(TAG, "readcount = " + Integer.toString(readcount));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				if (e.getMessage() == null || e.getMessage().equals("recvfrom failed: ETIMEDOUT (Connection timed out)")){
					reConnect(5173);	
					//Log.e(TAG, e.getMessage());
				}
				return false;
			} catch (IndexOutOfBoundsException e) {
				e.printStackTrace();
				reConnect(5173);
				return false;
			}
		}
		return true;

	}

	private int b2i(byte[] b) {
		int value = 0;
		for (int i = 0; i < 4; i++) {
			int shift = (4 - 1 - i) * 8;
			value += (b[i] & 0x000000FF) << shift;
		}
		return value;
	}

}